package cn.edu.scau.cmi.ema.service;

import cn.edu.scau.cmi.ema.base.CmiPagination;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Query;
import java.util.*;
@Service("FeatureService")
@Transactional

public class FeatureServiceImpl extends CmiEmaService implements FeatureService {
	public FeatureServiceImpl() {
	}

	@Transactional
	public Feature saveFeature(Feature feature) {
		Set<Detectdetail> existingDetectdetailsForFeature = new HashSet<Detectdetail>();
		Set<Detectfeature> existingDetectfeaturesForFeature = new HashSet<Detectfeature>();
		Set<Detectstandard> existingDetectstandardsForFeature = new HashSet<Detectstandard>();
		Set<Featurestandard> existingFeaturestandardsForFeature = new HashSet<Featurestandard>();

		Set<Detectdetail> selectedDetectdetailsForFeature = feature.getDetectdetailsForFeature();
		Set<Detectfeature> selectedDetectfeaturesForFeature = feature.getDetectfeaturesForFeature();
		Set<Detectstandard> selectedDetectstandardsForFeature = feature.getDetectstandardsForFeature();
		Set<Featurestandard> selectedFeaturestandardsForFeature = feature.getFeaturestandardsForFeature();

		if (feature.getId() == null) {
			Detectmethod detectmethod = detectmethodDAO.findDetectmethodById(feature.getDetectmethod().getId());

			Feature existingFeature = featureDAO.findFeatureByPhysicalPrimaryKey(feature.getName(),
					detectmethod.getId());

			feature.setDetectmethod(detectmethod);

			if (isEquals(feature, existingFeature)) {
				return existingFeature;
			}

			feature = featureDAO.store(feature);
			featureDAO.flush();

			for (Detectdetail relatedDetectdetail : selectedDetectdetailsForFeature) {
				relatedDetectdetail.setFeature(feature);
				detectdetailDAO.store(relatedDetectdetail);
			}
			for (Detectfeature relatedDetectfeature : selectedDetectfeaturesForFeature) {
				relatedDetectfeature.setFeature(feature);
				detectfeatureDAO.store(relatedDetectfeature);
			}
			for (Detectstandard relatedDetectstandard : selectedDetectstandardsForFeature) {
				relatedDetectstandard.setFeature(feature);
				detectstandardDAO.store(relatedDetectstandard);
			}
			for (Featurestandard relatedFeaturestandard : selectedFeaturestandardsForFeature) {
				relatedFeaturestandard.setFeature(feature);
				featurestandardDAO.store(relatedFeaturestandard);
			}
		} else {
			Feature existingFeature = featureDAO.findFeatureByPrimaryKey(feature.getId());

			existingDetectdetailsForFeature = existingFeature.getDetectdetailsForFeature();
			existingDetectfeaturesForFeature = existingFeature.getDetectfeaturesForFeature();
			existingDetectstandardsForFeature = existingFeature.getDetectstandardsForFeature();
			existingFeaturestandardsForFeature = existingFeature.getFeaturestandardsForFeature();

			Set<Detectdetail> prepareDeleteDetectdetailsForFeature = new HashSet<Detectdetail>();
			Set<Detectdetail> prepareAddDetectdetailsForFeature = new HashSet<Detectdetail>();

			prepareDeleteDetectdetailsForFeature.addAll(selectedDetectdetailsForFeature);
			prepareDeleteDetectdetailsForFeature.addAll(existingDetectdetailsForFeature);

			prepareAddDetectdetailsForFeature.addAll(selectedDetectdetailsForFeature);
			prepareAddDetectdetailsForFeature.addAll(existingDetectdetailsForFeature);

			prepareDeleteDetectdetailsForFeature.removeAll(selectedDetectdetailsForFeature);
			prepareAddDetectdetailsForFeature.removeAll(existingDetectdetailsForFeature);

			for (Detectdetail relatedDetectdetail : prepareAddDetectdetailsForFeature) {
				relatedDetectdetail.setFeature(feature);
				detectdetailDAO.store(relatedDetectdetail);
			}

			for (Detectdetail relatedDetectdetail : prepareDeleteDetectdetailsForFeature) {
				relatedDetectdetail.setFeature(null);
				detectdetailDAO.store(relatedDetectdetail);
			}
			Set<Detectfeature> prepareDeleteDetectfeaturesForFeature = new HashSet<Detectfeature>();
			Set<Detectfeature> prepareAddDetectfeaturesForFeature = new HashSet<Detectfeature>();

			prepareDeleteDetectfeaturesForFeature.addAll(selectedDetectfeaturesForFeature);
			prepareDeleteDetectfeaturesForFeature.addAll(existingDetectfeaturesForFeature);

			prepareAddDetectfeaturesForFeature.addAll(selectedDetectfeaturesForFeature);
			prepareAddDetectfeaturesForFeature.addAll(existingDetectfeaturesForFeature);

			prepareDeleteDetectfeaturesForFeature.removeAll(selectedDetectfeaturesForFeature);
			prepareAddDetectfeaturesForFeature.removeAll(existingDetectfeaturesForFeature);

			for (Detectfeature relatedDetectfeature : prepareAddDetectfeaturesForFeature) {
				relatedDetectfeature.setFeature(feature);
				detectfeatureDAO.store(relatedDetectfeature);
			}

			for (Detectfeature relatedDetectfeature : prepareDeleteDetectfeaturesForFeature) {
				relatedDetectfeature.setFeature(null);
				detectfeatureDAO.store(relatedDetectfeature);
			}
			Set<Detectstandard> prepareDeleteDetectstandardsForFeature = new HashSet<Detectstandard>();
			Set<Detectstandard> prepareAddDetectstandardsForFeature = new HashSet<Detectstandard>();

			prepareDeleteDetectstandardsForFeature.addAll(selectedDetectstandardsForFeature);
			prepareDeleteDetectstandardsForFeature.addAll(existingDetectstandardsForFeature);

			prepareAddDetectstandardsForFeature.addAll(selectedDetectstandardsForFeature);
			prepareAddDetectstandardsForFeature.addAll(existingDetectstandardsForFeature);

			prepareDeleteDetectstandardsForFeature.removeAll(selectedDetectstandardsForFeature);
			prepareAddDetectstandardsForFeature.removeAll(existingDetectstandardsForFeature);

			for (Detectstandard relatedDetectstandard : prepareAddDetectstandardsForFeature) {
				relatedDetectstandard.setFeature(feature);
				detectstandardDAO.store(relatedDetectstandard);
			}

			for (Detectstandard relatedDetectstandard : prepareDeleteDetectstandardsForFeature) {
				relatedDetectstandard.setFeature(null);
				detectstandardDAO.store(relatedDetectstandard);
			}
			Set<Featurestandard> prepareDeleteFeaturestandardsForFeature = new HashSet<Featurestandard>();
			Set<Featurestandard> prepareAddFeaturestandardsForFeature = new HashSet<Featurestandard>();

			prepareDeleteFeaturestandardsForFeature.addAll(selectedFeaturestandardsForFeature);
			prepareDeleteFeaturestandardsForFeature.addAll(existingFeaturestandardsForFeature);

			prepareAddFeaturestandardsForFeature.addAll(selectedFeaturestandardsForFeature);
			prepareAddFeaturestandardsForFeature.addAll(existingFeaturestandardsForFeature);

			prepareDeleteFeaturestandardsForFeature.removeAll(selectedFeaturestandardsForFeature);
			prepareAddFeaturestandardsForFeature.removeAll(existingFeaturestandardsForFeature);

			for (Featurestandard relatedFeaturestandard : prepareAddFeaturestandardsForFeature) {
				relatedFeaturestandard.setFeature(feature);
				featurestandardDAO.store(relatedFeaturestandard);
			}

			for (Featurestandard relatedFeaturestandard : prepareDeleteFeaturestandardsForFeature) {
				relatedFeaturestandard.setFeature(null);
				featurestandardDAO.store(relatedFeaturestandard);
			}
			feature = featureDAO.store(feature);
			featureDAO.flush();
		}

		detectdetailDAO.flush();
		detectfeatureDAO.flush();
		detectstandardDAO.flush();
		featurestandardDAO.flush();
		return feature;
	}

	public void deleteFeature(Feature feature) {
		featureDAO.remove(feature);
		featureDAO.flush();
	}

	@Transactional
	public void deleteFeatures(Set<Integer> deleteFeaturesId) {
		Query query = featureDAO.createNamedQuery("deleteFeaturesById", -1, -1);
		query.setParameter(1, deleteFeaturesId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Feature> loadFeatures() {
		return featureDAO.findAllFeatures();

	}

	@Transactional
	public Set<Feature> loadReferenceFeatures(Set<Feature> features) {
		Iterator<Feature> featuresIterator = features.iterator();
		Set<Feature> referencedFeatures = new HashSet<>();
		while (featuresIterator.hasNext()) {
			Feature currentFeature = featuresIterator.next();

			if (!currentFeature.getDetectdetailsForFeature().isEmpty()) {
				referencedFeatures.add(currentFeature);
				continue;
			}

			if (!currentFeature.getDetectfeaturesForFeature().isEmpty()) {
				referencedFeatures.add(currentFeature);
				continue;
			}

			if (!currentFeature.getDetectstandardsForFeature().isEmpty()) {
				referencedFeatures.add(currentFeature);
				continue;
			}

			if (!currentFeature.getFeaturestandardsForFeature().isEmpty()) {
				referencedFeatures.add(currentFeature);
				continue;
			}
		}
		return referencedFeatures;
	}

	public Set<Feature> loadFeaturesByIdSet(Set<Integer> featureIdSet) {

		Iterator<Integer> featureIdIterator = featureIdSet.iterator();
		Set<Feature> featureSet = new HashSet<>();
		while (featureIdIterator.hasNext()) {
			Integer id = featureIdIterator.next();
			Feature currentFeature = featureDAO.findFeatureById(id);
			featureSet.add(currentFeature);
		}
		return featureSet;
	}

	@Transactional
	public List<Feature> findAllFeatures(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Feature>(featureDAO.findAllFeatures(startResult, maxRows));
	}

	@Transactional
	public Feature findFeatureByPrimaryKey(Integer id) {
		return featureDAO.findFeatureByPrimaryKey(id);
	}

	@Transactional
	public Integer countFeatures() {
		return ((Long) featureDAO.createQuerySingleResult("select count(o) from Feature o").getSingleResult())
				.intValue();
	}

	@Transactional
	public Integer countFeatures(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Feature.class);
		return ((Long) featureDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 *
	 */
	@Transactional
	public Integer countFeaturesByName(String queryName) {
		Query query = featureDAO.createNamedQuery("countFeaturesByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countFeaturesByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Feature.class);
		Session session = (Session) entityManager.getDelegate();
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadFeatureObjectReferencedSet(Feature feature) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();
		if (feature.getDetectmethod() != null) {

			Set<Integer> detectmethodIds = new HashSet<Integer>();
			detectmethodIds.add(feature.getDetectmethod().getId());
			objectReferencedSetMap.put("detectmethodIds", detectmethodIds);
		}

		Set<Integer> detectdetailsIds = new HashSet<Integer>();
		for (Detectdetail obj : feature.getDetectdetailsForFeature()) {

			detectdetailsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("detectdetailsIds", detectdetailsIds);
		Set<Integer> detectfeaturesIds = new HashSet<Integer>();
		for (Detectfeature obj : feature.getDetectfeaturesForFeature()) {

			detectfeaturesIds.add(obj.getId());
		}
		objectReferencedSetMap.put("detectfeaturesIds", detectfeaturesIds);
		Set<Integer> detectstandardsIds = new HashSet<Integer>();
		for (Detectstandard obj : feature.getDetectstandardsForFeature()) {

			detectstandardsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("detectstandardsIds", detectstandardsIds);
		Set<Integer> featurestandardsIds = new HashSet<Integer>();
		for (Featurestandard obj : feature.getFeaturestandardsForFeature()) {

			featurestandardsIds.add(obj.getId());
		}
		objectReferencedSetMap.put("featurestandardsIds", featurestandardsIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadFeatureClassReferencedSet(Feature feature) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("detectmethods", detectmethodDAO.findAllDetectmethods());
		classReferencedSetMap.put("detectdetails", detectdetailDAO.findAllDetectdetails());
		classReferencedSetMap.put("detectfeatures", detectfeatureDAO.findAllDetectfeatures());
		classReferencedSetMap.put("detectstandards", detectstandardDAO.findAllDetectstandards());
		classReferencedSetMap.put("featurestandards", featurestandardDAO.findAllFeaturestandards());

		return classReferencedSetMap;
	}

	public boolean isEquals(Feature feature, Object obj) {
		if (obj == feature)
			return true;
		if (!(obj instanceof Feature))
			return false;
		Feature comparedFeature = (Feature) obj;
		if (!CheckPropertyEqual(feature.getName(), comparedFeature.getName())) {
			return false;
		}
		if (!CheckPropertyEqual(feature.getDetectmethod().getId(), comparedFeature.getDetectmethod().getId())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

